/***
    Amrita ITEWS - Copyright (C) 2009 Amrita Vishwa Vidyapeetham, Amritapuri.
                                       (http://www.amrita.edu)
    ***************************************************************************
    This file is part of the Amrita ITEWS distribution.
    Amrita ITEWS is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License (version 2) as published
    by the Free Software Foundation AND MODIFIED BY the Amrita ITEWS exception.
    ***NOTE*** The exception to the GPL is included to allow you to distribute
    a combined work that includes Amrita ITEWS without being obliged to provide
    the source code for proprietary components outside of the Amrita ITEWS
    software. Amrita ITEWS is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
    Public License for more details. You should have received a copy of the GNU
    General Public License and the Amrita ITEWS license exception along with
    Amrita ITEWS if not then it can be viewed here:
    http://itews.amrita.ac.in/license.html.

    Note: This software is closely coupled to FreeRTOS and hence the
    licensing terms of FreeRTOS would apply.


    Documentation, latest information, license and contact details are at:
    http://itews.amrita.ac.in/


    Amrita ITEWS source code can be found at:
    http://code.google.com/p/itews


    The initial code-base of Amrita ITEWS was developed by Amrita Vishwa
    Vidyapeetham as part of the project titled,"Intelligent & Interactive
    Telematics using Emerging Wireless Technologies for Transport Systems
    (ITEWS)" sponsored by Technology Information Forecasting and Assessment
    Council (TIFAC), India.
***/


#include <FreeRTOS.h>
#include <hw.h>
#include <board_config.h>
#include <igpsu.h>

#define VAL_PLL_MUL         (configCPU_CLOCK_MHZ/BOARD_CONFIG_FOSC_MHZ - 1)
/*Fcco = cclk * 2 * PLL_DIV. Fcc0 MUST BE from 156MHz to 320MHz. See PLL Sec. in LPC UM*/
#define VAL_FCCO_MIN_MHZ    156
#define VAL_FCCO_MAX_MHZ    320

#if configCPU_CLOCK_MHZ*1 >= VAL_FCCO_MIN_MHZ
#define VAL_PLL_DIV         0
#elif configCPU_CLOCK_MHZ*2 >= VAL_FCCO_MIN_MHZ
#define VAL_PLL_DIV         1
#elif configCPU_CLOCK_MHZ*4 >= VAL_FCCO_MIN_MHZ
#define VAL_PLL_DIV         2
#elif configCPU_CLOCK_MHZ*8 >= VAL_FCCO_MIN_MHZ
#define VAL_PLL_DIV         3
#else
#error "No valid PLL Divider value exists for selected CCLK/FOSC combination!"
#endif


/* Constants to setup I/O. */
#define VAL_TX_ENABLE   ( ( unsigned portLONG ) 0x0001 )
#define VAL_RX_ENABLE   ( ( unsigned portLONG ) 0x0004 )
#define VAL_P0_14       ( ( unsigned portLONG ) 0x4000 )

/* Constants to setup the PLL. */
#define VAL_PLL_DISABLE     ( ( unsigned portCHAR ) 0x0000 )
#define VAL_PLL_ENABLE      ( ( unsigned portCHAR ) 0x0001 )
#define VAL_PLL_CONNECT     ( ( unsigned portCHAR ) 0x0003 )
#define VAL_PLL_FEED_BYTE1  ( ( unsigned portCHAR ) 0xaa )
#define VAL_PLL_FEED_BYTE2  ( ( unsigned portCHAR ) 0x55 )
#define VAL_PLL_LOCK        ( ( unsigned portLONG ) 0x0400 )

/* Constants to setup the MAM. */
#define VAL_MAM_TIM_3       ( ( unsigned portCHAR ) 0x03 )
#define VAL_MAM_TIM_4       ( ( unsigned portCHAR ) 0x04 )
#define VAL_MAM_MODE_FULL   ( ( unsigned portCHAR ) 0x02 )
#define VAL_MAM_MODE_NONE   ( ( unsigned portCHAR ) 0x00 )

/* Constants to setup the peripheral bus. */
#define VAL_BUS_CLK_FULL    ( ( unsigned portCHAR ) 0x01 )
#define VAL_BUS_CLK_HALF    ( ( unsigned portCHAR ) 0x02 )
#define VAL_BUS_CLK_QUART   ( ( unsigned portCHAR ) 0x00 )

#define VAL_ENABLE_FIO      ( ( unsigned portLONG ) 0x03 )

void (*hw_app_fiq_handler)(void);


void hw_start_pll()
{

    /* Disable PLL first. */
    /*SCB_PLLCON = VAL_PLL_DISABLE;
    SCB_PLLFEED = VAL_PLL_FEED_BYTE1;
    SCB_PLLFEED = VAL_PLL_FEED_BYTE2;*/

    /* Setup the PLL to multiply the XTAL input. */
    SCB_PLLCFG = (unsigned portCHAR)( VAL_PLL_MUL | (VAL_PLL_DIV<<5) );
    SCB_PLLFEED = VAL_PLL_FEED_BYTE1;
    SCB_PLLFEED = VAL_PLL_FEED_BYTE2;

    /* Activate the PLL by turning it on then feeding the correct sequence of
    bytes. */
    SCB_PLLCON = VAL_PLL_ENABLE;
    SCB_PLLFEED = VAL_PLL_FEED_BYTE1;
    SCB_PLLFEED = VAL_PLL_FEED_BYTE2;

    /* Wait for the PLL to lock... */
    while( !( SCB_PLLSTAT & VAL_PLL_LOCK ) );

    /* ...before connecting it using the feed sequence again. */
    SCB_PLLCON = VAL_PLL_CONNECT;
    SCB_PLLFEED = VAL_PLL_FEED_BYTE1;
    SCB_PLLFEED = VAL_PLL_FEED_BYTE2;

    /* Setup and turn on the MAM.  Three cycle access is used due to the fast
    PLL used.  It is possible faster overall performance could be obtained by
    tuning the MAM and PLL settings. */
    MAM_CR = VAL_MAM_MODE_NONE;
    MAM_TIM = VAL_MAM_TIM_4;
    MAM_CR = VAL_MAM_MODE_FULL;

    /* Setup the peripheral bus to be the same as the PLL output. */
    SCB_VPBDIV = VAL_BUS_CLK_FULL;

}

int hw_init(int run_from_ram)
{
    if(run_from_ram)
    {
        /* Remap the interrupt vectors to RAM if we are are running from RAM. */
        SCB_MEMMAP = 2;
    }else
    {
        SCB_MEMMAP = 1;
    }

    POWER_ON(PCONP_TIM0);//Timer0 for multitasking
    POWER_ON(PCONP_USB); //we're using USB memory

    SCS = 0;//no fast GPIO

    hw_start_pll();

    board_config();

    return 0;
}


void hw_fiq_handler()
{
    if(hw_app_fiq_handler != 0UL)
    {
        (*hw_app_fiq_handler)();
    }

    asm volatile("SUBS PC, LR, #4   \n\t");
}
